home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / greasemonkey-0.8.20080609.0-fx.xpi / chrome / greasemonkey.jar / chromeFiles / content / config.js < prev    next >
Text File  |  2008-06-09  |  20KB  |  606 lines

  1. function Config() {
  2.   this._scripts = null;
  3.   this._configFile = this._scriptDir;
  4.   this._configFile.append("config.xml");
  5.   this._initScriptDir();
  6.  
  7.   this._observers = [];
  8.  
  9.   this._updateVersion();
  10.   this._load();
  11. }
  12.  
  13. Config.prototype = {
  14.   addObserver: function(observer, script) {
  15.     var observers = script ? script._observers : this._observers;
  16.     observers.push(observer);
  17.   },
  18.  
  19.   removeObserver: function(observer, script) {
  20.     var observers = script ? script._observers : this._observers;
  21.     var index = observers.indexOf(observer);
  22.     if (index == -1) throw new Error("Observer not found");
  23.     observers.splice(index, 1);
  24.   },
  25.  
  26.   _notifyObservers: function(script, event, data) {
  27.     var observers = this._observers.concat(script._observers);
  28.     for (var i = 0, observer; observer = observers[i]; i++) {
  29.       observer.notifyEvent(script, event, data);
  30.     }
  31.   },
  32.  
  33.   _changed: function(script, event, data) {
  34.     this._save();
  35.     this._notifyObservers(script, event, data);
  36.   },
  37.  
  38.   installIsUpdate: function(script) {
  39.     return this._find(script) > -1;
  40.   },
  41.  
  42.   _find: function(aScript) {
  43.     namespace = aScript._namespace.toLowerCase();
  44.     name = aScript._name.toLowerCase();
  45.  
  46.     for (var i = 0, script; script = this._scripts[i]; i++) {
  47.       if (script._namespace.toLowerCase() == namespace
  48.         && script._name.toLowerCase() == name) {
  49.         return i;
  50.       }
  51.     }
  52.  
  53.     return -1;
  54.   },
  55.  
  56.   _load: function() {
  57.     var domParser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
  58.                               .createInstance(Components.interfaces.nsIDOMParser);
  59.  
  60.     var configContents = getContents(this._configFile);
  61.     var doc = domParser.parseFromString(configContents, "text/xml");
  62.     var nodes = doc.evaluate("/UserScriptConfig/Script", doc, null, 0, null);
  63.  
  64.     this._scripts = [];
  65.  
  66.     for (var node = null; node = nodes.iterateNext(); ) {
  67.       var script = new Script(this);
  68.  
  69.       for (var i = 0, childNode; childNode = node.childNodes[i]; i++) {
  70.         switch (childNode.nodeName) {
  71.         case "Include":
  72.           script._includes.push(childNode.firstChild.nodeValue);
  73.           break;
  74.         case "Exclude":
  75.           script._excludes.push(childNode.firstChild.nodeValue);
  76.           break;
  77.         case "Require":
  78.           var scriptRequire = new ScriptRequire(script);
  79.           scriptRequire._filename = childNode.getAttribute("filename");
  80.           script._requires.push(scriptRequire);
  81.           break;
  82.         case "Resource":
  83.           var scriptResource = new ScriptResource(script);
  84.           scriptResource._name = childNode.getAttribute("name");
  85.           scriptResource._filename = childNode.getAttribute("filename");
  86.           scriptResource._mimetype = childNode.getAttribute("mimetype");
  87.           scriptResource._charset = childNode.getAttribute("charset");
  88.           script._resources.push(scriptResource);
  89.           break;
  90.         }
  91.       }
  92.  
  93.       script._filename = node.getAttribute("filename");
  94.       script._name = node.getAttribute("name");
  95.       script._namespace = node.getAttribute("namespace");
  96.       script._description = node.getAttribute("description");
  97.       script._enabled = node.getAttribute("enabled") == true.toString();
  98.       script._basedir = node.getAttribute("basedir") || ".";
  99.  
  100.       this._scripts.push(script);
  101.     }
  102.   },
  103.  
  104.   _save: function() {
  105.     var doc = Components.classes["@mozilla.org/xmlextras/domparser;1"]
  106.       .createInstance(Components.interfaces.nsIDOMParser)
  107.       .parseFromString("<UserScriptConfig></UserScriptConfig>", "text/xml");
  108.  
  109.     for (var i = 0, scriptObj; scriptObj = this._scripts[i]; i++) {
  110.       var scriptNode = doc.createElement("Script");
  111.  
  112.       for (var j = 0; j < scriptObj._includes.length; j++) {
  113.         var includeNode = doc.createElement("Include");
  114.         includeNode.appendChild(doc.createTextNode(scriptObj._includes[j]));
  115.         scriptNode.appendChild(doc.createTextNode("\n\t\t"));
  116.         scriptNode.appendChild(includeNode);
  117.       }
  118.  
  119.       for (var j = 0; j < scriptObj._excludes.length; j++) {
  120.         var excludeNode = doc.createElement("Exclude");
  121.         excludeNode.appendChild(doc.createTextNode(scriptObj._excludes[j]));
  122.         scriptNode.appendChild(doc.createTextNode("\n\t\t"));
  123.         scriptNode.appendChild(excludeNode);
  124.       }
  125.  
  126.       for (var j = 0; j < scriptObj._requires.length; j++) {
  127.         var req = scriptObj._requires[j];
  128.         var resourceNode = doc.createElement("Require");
  129.  
  130.         resourceNode.setAttribute("filename", req._filename);
  131.  
  132.         scriptNode.appendChild(doc.createTextNode("\n\t\t"));
  133.         scriptNode.appendChild(resourceNode);
  134.       }
  135.  
  136.       for (var j = 0; j < scriptObj._resources.length; j++) {
  137.         var imp = scriptObj._resources[j];
  138.         var resourceNode = doc.createElement("Resource");
  139.  
  140.         resourceNode.setAttribute("name", imp._name);
  141.         resourceNode.setAttribute("filename", imp._filename);
  142.         resourceNode.setAttribute("mimetype", imp._mimetype);
  143.         if (imp._charset) {
  144.           resourceNode.setAttribute("charset", imp._charset);
  145.         }
  146.  
  147.         scriptNode.appendChild(doc.createTextNode("\n\t\t"));
  148.         scriptNode.appendChild(resourceNode);
  149.       }
  150.  
  151.       scriptNode.appendChild(doc.createTextNode("\n\t"));
  152.  
  153.       scriptNode.setAttribute("filename", scriptObj._filename);
  154.       scriptNode.setAttribute("name", scriptObj._name);
  155.       scriptNode.setAttribute("namespace", scriptObj._namespace);
  156.       scriptNode.setAttribute("description", scriptObj._description);
  157.       scriptNode.setAttribute("enabled", scriptObj._enabled);
  158.       scriptNode.setAttribute("basedir", scriptObj._basedir);
  159.  
  160.       doc.firstChild.appendChild(doc.createTextNode("\n\t"));
  161.       doc.firstChild.appendChild(scriptNode);
  162.     }
  163.  
  164.     doc.firstChild.appendChild(doc.createTextNode("\n"));
  165.  
  166.     var configStream = getWriteStream(this._configFile);
  167.     Components.classes["@mozilla.org/xmlextras/xmlserializer;1"]
  168.       .createInstance(Components.interfaces.nsIDOMSerializer)
  169.       .serializeToStream(doc, configStream, "utf-8");
  170.     configStream.close();
  171.   },
  172.  
  173.   parse: function(source, uri) {
  174.     var ioservice = Components.classes["@mozilla.org/network/io-service;1"]
  175.                               .getService(Components.interfaces.nsIIOService);
  176.  
  177.     var script = new Script(this);
  178.     script._downloadURL = uri.spec;
  179.     script._enabled = true;
  180.  
  181.     // read one line at a time looking for start meta delimiter or EOF
  182.     var lines = source.match(/.+/g);
  183.     var lnIdx = 0;
  184.     var result = {};
  185.     var foundMeta = false;
  186.  
  187.     while ((result = lines[lnIdx++])) {
  188.       if (result.indexOf("// ==UserScript==") == 0) {
  189.         foundMeta = true;
  190.         break;
  191.       }
  192.     }
  193.  
  194.     // gather up meta lines
  195.     if (foundMeta) {
  196.       // used for duplicate resource name detection
  197.       var previousResourceNames = {};
  198.  
  199.       while ((result = lines[lnIdx++])) {
  200.         if (result.indexOf("// ==/UserScript==") == 0) {
  201.           break;
  202.         }
  203.  
  204.         var match = result.match(/\/\/ \@(\S+)\s+([^\n]+)/);
  205.         if (match != null) {
  206.           switch (match[1]) {
  207.             case "name":
  208.             case "namespace":
  209.             case "description":
  210.               script["_" + match[1]] = match[2];
  211.               break;
  212.             case "include":
  213.               script._includes.push(match[2]);
  214.               break;
  215.             case "exclude":
  216.               script._excludes.push(match[2]);
  217.               break;
  218.             case "require":
  219.               var reqUri = ioservice.newURI(match[2], null, uri);
  220.               var scriptRequire = new ScriptRequire(script);
  221.               scriptRequire._downloadURL = reqUri.spec;
  222.               script._requires.push(scriptRequire);
  223.               break;
  224.             case "resource":
  225.               var res = match[2].match(/(\S+)\s+(.*)/);
  226.               if (res === null) {
  227.                 // NOTE: Unlocalized strings
  228.                 throw new Error("Invalid syntax for @resource declaration '" +
  229.                                 match[2] + "'. Resources are declared like: " +
  230.                                 "@resource <name> <url>.");
  231.               }
  232.  
  233.               var resName = res[1];
  234.               if (previousResourceNames[resName]) {
  235.                 throw new Error("Duplicate resource name '" + resName + "' " +
  236.                                 "detected. Each resource must have a unique " +
  237.                                 "name.");
  238.               } else {
  239.                 previousResourceNames[resName] = true;
  240.               }
  241.  
  242.               var resUri = ioservice.newURI(res[2], null, uri);
  243.               var scriptResource = new ScriptResource(script);
  244.               scriptResource._name = resName;
  245.               scriptResource._downloadURL = resUri.spec;
  246.               script._resources.push(scriptResource);
  247.               break;
  248.           }
  249.         }
  250.       }
  251.     }
  252.  
  253.     // if no meta info, default to reasonable values
  254.     if (script._name == null) script._name = parseScriptName(uri);
  255.     if (script._namespace == null) script._namespace = uri.host;
  256.     if (!script._description) script._description = "";
  257.     if (script._includes.length == 0) script._includes.push("*");
  258.  
  259.     return script;
  260.   },
  261.  
  262.   install: function(script) {
  263.     GM_log("> Config.install");
  264.  
  265.     var existingIndex = this._find(script);
  266.     if (existingIndex > -1) {
  267.       this.uninstall(this._scripts[existingIndex], false);
  268.     }
  269.  
  270.     script._initFile(script._tempFile);
  271.     script._tempFile = null;
  272.  
  273.     for (var i = 0; i < script._requires.length; i++) {
  274.       script._requires[i]._initFile();
  275.     }
  276.  
  277.     for (var i = 0; i < script._resources.length; i++) {
  278.       script._resources[i]._initFile();
  279.     }
  280.  
  281.     this._scripts.push(script);
  282.     this._changed(script, "install", null);
  283.  
  284.     GM_log("< Config.install");
  285.   },
  286.  
  287.   uninstall: function(script, uninstallPrefs) {
  288.     var idx = this._find(script);
  289.     this._scripts.splice(idx, 1);
  290.     this._changed(script, "uninstall", null);
  291.  
  292.     // watch out for cases like basedir="." and basedir="../gm_scripts"
  293.     if (!script._basedirFile.equals(this._scriptDir)) {
  294.       // if script has its own dir, remove the dir + contents
  295.       script._basedirFile.remove(true);
  296.     } else {
  297.       // if script is in the root, just remove the file
  298.       script._file.remove(false);
  299.     }
  300.  
  301.     if (uninstallPrefs) {
  302.       // Remove saved preferences
  303.       GM_prefRoot.remove("scriptvals." + script._namespace + "/" + script._name + ".");
  304.     }
  305.   },
  306.  
  307.   /**
  308.    * Moves an installed user script to a new position in the array of installed scripts.
  309.    *
  310.    * @param script The script to be moved.
  311.    * @param destination Can be either (a) a numeric offset for the script to be
  312.    *                    moved or (b) another installet script to which position
  313.    *                    the script will be moved.
  314.    */
  315.   move: function(script, destination) {
  316.     var from = this._scripts.indexOf(script);
  317.     var to = -1;
  318.  
  319.     // Make sure the user script is installed
  320.     if (from == -1) return;
  321.  
  322.     if (typeof destination == "number") { // if destination is an offset
  323.       to = from + destination;
  324.       to = Math.max(0, to);
  325.       to = Math.min(this._scripts.length - 1, to);
  326.     } else { // if destination is a script object
  327.       to = this._scripts.indexOf(destination);
  328.     }
  329.  
  330.     if (to == -1) return;
  331.  
  332.     var tmp = this._scripts.splice(from, 1)[0];
  333.     this._scripts.splice(to, 0, tmp);
  334.     this._changed(script, "move", to);
  335.   },
  336.  
  337.   get _scriptDir() {
  338.     var file = Components.classes["@mozilla.org/file/directory_service;1"]
  339.                          .getService(Components.interfaces.nsIProperties)
  340.                          .get("ProfD", Components.interfaces.nsILocalFile);
  341.     file.append("gm_scripts");
  342.     return file;
  343.   },
  344.  
  345.   /**
  346.    * Create an empty configuration if none exist.
  347.    */
  348.   _initScriptDir: function() {
  349.     var dir = this._scriptDir;
  350.  
  351.     if (!dir.exists()) {
  352.       dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
  353.  
  354.       var configStream = getWriteStream(this._configFile);
  355.       var xml = "<UserScriptConfig/>";
  356.       configStream.write(xml, xml.length);
  357.       configStream.close();
  358.     }
  359.   },
  360.  
  361.   get scripts() { return this._scripts.concat(); },
  362.   getMatchingScripts: function(testFunc) { return this._scripts.filter(testFunc); },
  363.  
  364.   /**
  365.    * Checks whether the version has changed since the last run and performs
  366.    * any necessary upgrades.
  367.    */
  368.   _updateVersion: function() {
  369.     log("> GM_updateVersion");
  370.  
  371.     // this is the last version which has been run at least once
  372.     var initialized = GM_prefRoot.getValue("version", "0.0");
  373.  
  374.     if (GM_compareVersions(initialized, "0.8") == -1)
  375.       this._pointEightBackup();
  376.  
  377.     // update the currently initialized version so we don't do this work again.
  378.     var extMan = Components.classes["@mozilla.org/extensions/manager;1"]
  379.       .getService(Components.interfaces.nsIExtensionManager);
  380.  
  381.     var item = extMan.getItemForID(GM_GUID);
  382.     GM_prefRoot.setValue("version", item.version);
  383.  
  384.     log("< GM_updateVersion");
  385.   },
  386.  
  387.   /**
  388.    * In Greasemonkey 0.8 there was a format change to the gm_scripts folder and
  389.    * testing found several bugs where the entire folder would get nuked. So we
  390.    * are paranoid and backup the folder the first time 0.8 runs.
  391.    */
  392.   _pointEightBackup: function() {
  393.     var scriptDir = this._scriptDir;
  394.     var scriptDirBackup = scriptDir.clone();
  395.     scriptDirBackup.leafName += "_08bak";
  396.     if (scriptDir.exists() && !scriptDirBackup.exists())
  397.       scriptDir.copyTo(scriptDirBackup.parent, scriptDirBackup.leafName);
  398.   }
  399. };
  400.  
  401. function Script(config) {
  402.   this._config = config;
  403.   this._observers = [];
  404.  
  405.   this._downloadURL = null; // Only for scripts not installed
  406.   this._tempFile = null; // Only for scripts not installed
  407.   this._basedir = null;
  408.   this._filename = null;
  409.  
  410.   this._name = null;
  411.   this._namespace = null;
  412.   this._description = null;
  413.   this._enabled = true;
  414.   this._includes = [];
  415.   this._excludes = [];
  416.   this._requires = [];
  417.   this._resources = [];
  418. }
  419.  
  420. Script.prototype = {
  421.   matchesURL: function(url) {
  422.     function test(page) {
  423.       return convert2RegExp(page).test(url);
  424.     }
  425.  
  426.     return this._includes.some(test) && !this._excludes.some(test);
  427.   },
  428.  
  429.   _changed: function(event, data) { this._config._changed(this, event, data); },
  430.  
  431.   get name() { return this._name; },
  432.   get namespace() { return this._namespace; },
  433.   get description() { return this._description; },
  434.   get enabled() { return this._enabled; },
  435.   set enabled(enabled) { this._enabled = enabled; this._changed("edit-enabled", enabled); },
  436.  
  437.   get includes() { return this._includes.concat(); },
  438.   get excludes() { return this._excludes.concat(); },
  439.   addInclude: function(url) { this._includes.push(url); this._changed("edit-include-add", url); },
  440.   removeIncludeAt: function(index) { this._includes.splice(index, 1); this._changed("edit-include-remove", index); },
  441.   addExclude: function(url) { this._excludes.push(url); this._changed("edit-exclude-add", url); },
  442.   removeExcludeAt: function(index) { this._excludes.splice(index, 1); this._changed("edit-exclude-remove", index); },
  443.  
  444.   get requires() { return this._requires.concat(); },
  445.   get resources() { return this._resources.concat(); },
  446.  
  447.   get _file() {
  448.     var file = this._basedirFile;
  449.     file.append(this._filename);
  450.     return file;
  451.   },
  452.  
  453.   get editFile() { return this._file; },
  454.  
  455.   get _basedirFile() {
  456.     var file = this._config._scriptDir;
  457.     file.append(this._basedir);
  458.     file.normalize();
  459.     return file;
  460.   },
  461.  
  462.   get fileURL() { return GM_getUriFromFile(this._file).spec; },
  463.   get textContent() { return getContents(this._file); },
  464.  
  465.   _initFileName: function(name, useExt) {
  466.     var ext = "";
  467.     name = name.toLowerCase();
  468.  
  469.     var dotIndex = name.lastIndexOf(".");
  470.     if (dotIndex > 0 && useExt) {
  471.       ext = name.substring(dotIndex + 1);
  472.       name = name.substring(0, dotIndex);
  473.     }
  474.  
  475.     name = name.replace(/\s+/g, "_").replace(/[^-_A-Z0-9]+/gi, "");
  476.     ext = ext.replace(/\s+/g, "_").replace(/[^-_A-Z0-9]+/gi, "");
  477.  
  478.     // If no Latin characters found - use default
  479.     if (!name) name = "gm_script";
  480.  
  481.     // 24 is a totally arbitrary max length
  482.     if (name.length > 24) name = name.substring(0, 24);
  483.  
  484.     if (ext) name += "." + ext;
  485.  
  486.     return name;
  487.   },
  488.  
  489.   _initFile: function(tempFile) {
  490.     var file = this._config._scriptDir;
  491.     var name = this._initFileName(this._name, false);
  492.  
  493.     file.append(name);
  494.     file.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
  495.     this._basedir = file.leafName;
  496.  
  497.     file.append(name + ".user.js");
  498.     file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644);
  499.     this._filename = file.leafName;
  500.  
  501.     GM_log("Moving script file from " + tempFile.path + " to " + file.path);
  502.  
  503.     file.remove(true);
  504.     tempFile.moveTo(file.parent, file.leafName);
  505.   },
  506.  
  507.   get urlToDownload() { return this._downloadURL; },
  508.   setDownloadedFile: function(file) { this._tempFile = file; },
  509.  
  510.   get previewURL() {
  511.     return Components.classes["@mozilla.org/network/io-service;1"]
  512.                      .getService(Components.interfaces.nsIIOService)
  513.                      .newFileURI(this._tempFile).spec;
  514.   }
  515. };
  516.  
  517. function ScriptRequire(script) {
  518.   this._script = script;
  519.  
  520.   this._downloadURL = null; // Only for scripts not installed
  521.   this._tempFile = null; // Only for scripts not installed
  522.   this._filename = null;
  523. }
  524.  
  525. ScriptRequire.prototype = {
  526.   get _file() {
  527.     var file = this._script._basedirFile;
  528.     file.append(this._filename);
  529.     return file;
  530.   },
  531.  
  532.   get fileURL() { return GM_getUriFromFile(this._file).spec; },
  533.   get textContent() { return getContents(this._file); },
  534.  
  535.   _initFile: function() {
  536.     var name = this._downloadURL.substr(this._downloadURL.lastIndexOf("/") + 1);
  537.     if(name.indexOf("?") > 0) {
  538.       name = name.substr(0, name.indexOf("?"));
  539.     }
  540.     name = this._script._initFileName(name, true);
  541.  
  542.     var file = this._script._basedirFile;
  543.     file.append(name);
  544.     file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644);
  545.     this._filename = file.leafName;
  546.  
  547.     GM_log("Moving dependency file from " + this._tempFile.path + " to " + file.path);
  548.  
  549.     file.remove(true);
  550.     this._tempFile.moveTo(file.parent, file.leafName);
  551.     this._tempFile = null;
  552.   },
  553.  
  554.   get urlToDownload() { return this._downloadURL; },
  555.   setDownloadedFile: function(file) { this._tempFile = file; }
  556. };
  557.  
  558. function ScriptResource(script) {
  559.   this._script = script;
  560.  
  561.   this._downloadURL = null; // Only for scripts not installed
  562.   this._tempFile = null; // Only for scripts not installed
  563.   this._filename = null;
  564.   this._mimetype = null;
  565.   this._charset = null;
  566.  
  567.   this._name = null;
  568. }
  569.  
  570. ScriptResource.prototype = {
  571.   get name() { return this._name; },
  572.  
  573.   get _file() {
  574.     var file = this._script._basedirFile;
  575.     file.append(this._filename);
  576.     return file;
  577.   },
  578.  
  579.   get textContent() { return getContents(this._file); },
  580.  
  581.   get dataContent() {
  582.     var appSvc = Components.classes["@mozilla.org/appshell/appShellService;1"]
  583.                            .getService(Components.interfaces.nsIAppShellService);
  584.  
  585.     var window = appSvc.hiddenDOMWindow;
  586.     var binaryContents = getBinaryContents(this._file);
  587.  
  588.     var mimetype = this._mimetype;
  589.     if (this._charset && this._charset.length > 0) {
  590.       mimetype += ";charset=" + this._charset;
  591.     }
  592.  
  593.     return "data:" + mimetype + ";base64," +
  594.       window.encodeURIComponent(window.btoa(binaryContents));
  595.   },
  596.  
  597.   _initFile: ScriptRequire.prototype._initFile,
  598.  
  599.   get urlToDownload() { return this._downloadURL; },
  600.   setDownloadedFile: function(tempFile, mimetype, charset) {
  601.     this._tempFile = tempFile;
  602.     this._mimetype = mimetype;
  603.     this._charset = charset;
  604.   }
  605. };
  606.